﻿using Bouyei.Geo.Geometries;
using System;
using System.IO;
using System.Security;

namespace Bouyei.Geo.Converters
{
    public class BitExtensions
    {
        public ByteOrder ByteOrder { get; set; }

        public BitExtensions()
        { }

        public BitExtensions(ByteOrder byteOrder)
        {
            this.ByteOrder = byteOrder;
        }

        public unsafe int WriteTo(byte* ptr,byte value)
        {
            *(ptr) = value;
            return 1;
        }

        public unsafe int WriteTo(byte* ptr, byte[] src)
        {
            int len = src.Length;
            for (int i = 0; i < len; ++i)
            {
                *(ptr + i) = src[i];
            }
            return len;
        }

        public unsafe void WriteTo(byte* ptr, uint value)
        {
            var array = GetBytes(value);

            *(ptr) = array[0];
            *(ptr + 1) = array[1];
            *(ptr + 2) = array[2];
            *(ptr + 3) = array[3];
        }

        public unsafe void WriteTo(byte* ptr, int value)
        {
            WriteTo(ptr, (uint)value);
        }

        public unsafe void WriteTo(byte* ptr, ulong value)
        {
            var array = GetBytes((long)value);
            WriteTo(ptr, array);
        }

        [SecuritySafeCritical]
        public unsafe void WriteTo(byte *ptr,double value)
        {
            var array = GetBytes(value);
            WriteTo(ptr, array);
        }

        [SecuritySafeCritical]
        public unsafe Int16 ToInt16(byte* ptr)
        {
            if (ByteOrder==ByteOrder.LittleEndian)
                return (short)((int)(*ptr) | (int)ptr[1] << 8);

            return (short)((int)(*ptr) << 8 | (int)ptr[1]);
        }

        [SecuritySafeCritical]
        public unsafe Int32 ToInt32(byte* ptr)
        {
            if (ByteOrder == ByteOrder.LittleEndian)
                return (int)(*ptr) | (int)ptr[1] << 8 | (int)ptr[2] << 16 | (int)ptr[3] << 24;

            return (int)(*ptr) << 24 | (int)ptr[1] << 16 | (int)ptr[2] << 8 | (int)ptr[3];
        }

        [SecuritySafeCritical]
        public unsafe Int64 ToInt64(byte *ptr)
        {
            //double d = BitConverter.ToDouble(new byte[] { *ptr, ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7] }, 0);

            if (ByteOrder == ByteOrder.LittleEndian)
            {
                int num = *ptr | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
                int num2 = ptr[4] | (ptr[5] << 8) | (ptr[6] << 16) | (ptr[7] << 24);
                return (uint)num | ((long)num2 << 32);
            }
            int num3 = (*ptr << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
            int num4 = (ptr[4] << 24) | (ptr[5] << 16) | (ptr[6] << 8) | ptr[7];
            return (uint)num4 | ((long)num3 << 32);
        }

        [SecuritySafeCritical]
        public unsafe float ToSingle(byte *ptr)
        {
            float val = ToInt16(ptr);
            return *(float*)(&val);
        }

        [SecuritySafeCritical]
        public unsafe byte[] GetBytes(int value)
        {
            byte[] array = new byte[4];
            fixed (byte* ptr = array)
            {
                *(int*)ptr = value;
            }
            return array;
        }

        [SecuritySafeCritical]
        public unsafe byte[] GetBytes(long value)
        {
            byte[] array = new byte[8];
            fixed (byte* ptr = array)
            {
                *(long*)ptr = value;
            }
            return array;
        }

        [SecuritySafeCritical]
        public unsafe byte[] GetBytes(double value)
        {
            return GetBytes(*(long*)&value);
        }

        [SecuritySafeCritical]
        public unsafe byte[] GetBytes(float value)
        {
            return GetBytes(*(int*)&value);
        }

        [SecuritySafeCritical]
        public unsafe double ToDouble(byte* ptr)
        {
            //double d = BitConverter.ToDouble(new byte[] { *ptr, ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7] }, 0);
            var val = ToInt64(ptr);
            return *(double*)(&val);
        }

        [SecuritySafeCritical]
        public unsafe Int16 ToInt16(byte[] array, int position = 0)
        {
            fixed (byte* ptr = &array[position])
            {
                return ToInt16(ptr);
            }
        }

        [SecuritySafeCritical]
        public unsafe Int32 ToInt32(byte[] array, int position = 0)
        {
            fixed (byte* ptr = &array[position])
            {
                //float d = BitConverter.ToSingle(new byte[] { *ptr, ptr[1], ptr[2], ptr[3] }, 0);
                return ToInt32(ptr);
            }
        }

        [SecuritySafeCritical]
        public unsafe Int64 ToInt64(byte[] array, int position = 0)
        {
            fixed (byte* ptr = &array[position])
            {
                return ToInt64(ptr);
            }
        }

        [SecuritySafeCritical]
        public unsafe double ToDouble(byte[] array, int position = 0)
        {
            long val = ToInt64(array, position);
            return *(double*)(&val);
        }

        [SecuritySafeCritical]
        public unsafe float ToSingle(byte[] array,int position=0)
        {
            int val = ToInt32(array, position);
            return *(float*)(&val);
        }

        public Int16 ToInt16(BinaryReader reader)
        {
            byte[] array = reader.ReadBytes(2);

            return ToInt16(array);
        }
 
        public Int32 ToInt32(BinaryReader reader)
        {
            byte[] array = reader.ReadBytes(4);

            return ToInt32(array);
        }
 
        public Int64 ToInt64(BinaryReader reader)
        {
            byte[] array = reader.ReadBytes(8);

            return ToInt64(array);
        }
    }
}
